package cn.jingyuan.bee.utils;

import cn.jingyuan.bee.utils.map.TableMap;

import java.lang.reflect.*;
import java.util.Map;

/**
 * 针对 {@link Type} 的工具类封装<br>
 * 最主要功能包括：
 *
 * <pre>
 * 1. 获取方法的参数和返回值类型（包括 Type 和 Class）
 * 2. 获取泛型参数类型（包括对象的泛型参数或集合元素的泛型类型）
 * </pre>
 */
public class TypeUtils {

    /**
     * 获得 Type 对应的原始类
     *
     * @param type {@link Type}
     *
     * @return 原始类，如果无法获取原始类，返回 {@code null}
     */
    public static Class<?> getClass(Type type) {
        if (null != type) {
            if (type instanceof Class) {
                return (Class<?>) type;
            } else if (type instanceof ParameterizedType) {
                return (Class<?>) ((ParameterizedType) type).getRawType();
            } else if (type instanceof TypeVariable) {
                return (Class<?>) ((TypeVariable<?>) type).getBounds()[0];
            } else if (type instanceof WildcardType) {
                final Type[] upperBounds = ((WildcardType) type).getUpperBounds();
                if (upperBounds.length == 1) {
                    return getClass(upperBounds[0]);
                }
            }
        }
        return null;
    }

    /**
     * 获取字段对应的 Type 类型<br>
     * 方法优先获取 GenericType，获取不到则获取 Type
     *
     * @param field 字段
     *
     * @return {@link Type}，可能为 {@code null}
     */
    public static Type getType(Field field) {
        if (null == field) {
            return null;
        }
        Type type = field.getGenericType();
        if (null == type) {
            type = field.getType();
        }
        return type;
    }

    /**
     * 获得 Field 对应的原始类
     *
     * @param field {@link Field}
     *
     * @return 原始类，如果无法获取原始类，返回 {@code null}
     */
    public static Class<?> getClass(Field field) {
        return null == field ? null : field.getType();
    }

    /**
     * 获取方法的第一个参数类型<br>
     * 优先获取方法的 GenericParameterTypes，如果获取不到，则获取 ParameterTypes
     *
     * @param method 方法
     *
     * @return {@link Type}，可能为 {@code null}
     */
    public static Type getFirstParamType(Method method) {
        return getParamType(method, 0);
    }

    /**
     * 获取方法的第一个参数类
     *
     * @param method 方法
     *
     * @return 第一个参数类型，可能为 {@code null}
     */
    public static Class<?> getFirstParamClass(Method method) {
        return getParamClass(method, 0);
    }

    /**
     * 获取方法的参数类型<br>
     * 优先获取方法的 GenericParameterTypes，如果获取不到，则获取 ParameterTypes
     *
     * @param method 方法
     * @param index 第几个参数的索引，从 0 开始计数
     *
     * @return {@link Type}，可能为 {@code null}
     */
    public static Type getParamType(Method method, int index) {
        Type[] types = getParamTypes(method);
        if (null != types && types.length > index) {
            return types[index];
        }
        return null;
    }

    /**
     * 获取方法的参数类
     *
     * @param method 方法
     * @param index 第几个参数的索引，从 0 开始计数
     *
     * @return 参数类，可能为 {@code null}
     */
    public static Class<?> getParamClass(Method method, int index) {
        Class<?>[] classes = getParamClasses(method);
        if (null != classes && classes.length > index) {
            return classes[index];
        }
        return null;
    }

    /**
     * 获取方法的参数类型列表<br>
     * 优先获取方法的 GenericParameterTypes，如果获取不到，则获取 ParameterTypes
     *
     * @param method 方法
     *
     * @return {@link Type} 列表，可能为 {@code null}
     *
     * @see Method#getGenericParameterTypes()
     * @see Method#getParameterTypes()
     */
    public static Type[] getParamTypes(Method method) {
        return null == method ? null : method.getGenericParameterTypes();
    }

    /**
     * 解析方法的参数类型列表<br>
     * 依赖 jre\lib\rt.jar
     *
     * @param method t 方法
     *
     * @return 参数类型类列表
     *
     * @see Method#getGenericParameterTypes
     * @see Method#getParameterTypes
     */
    public static Class<?>[] getParamClasses(Method method) {
        return null == method ? null : method.getParameterTypes();
    }

    /**
     * 获取方法的返回值类型<br>
     * 获取方法的 GenericReturnType
     *
     * @param method 方法
     *
     * @return {@link Type}，可能为 {@code null}
     *
     * @see Method#getGenericReturnType()
     * @see Method#getReturnType()
     */
    public static Type getReturnType(Method method) {
        return null == method ? null : method.getGenericReturnType();
    }

    /**
     * 解析方法的返回类型类列表
     *
     * @param method 方法
     *
     * @return 返回值类型的类
     *
     * @see Method#getGenericReturnType
     * @see Method#getReturnType
     */
    public static Class<?> getReturnClass(Method method) {
        return null == method ? null : method.getReturnType();
    }

    /**
     * 获得给定类的第一个泛型参数
     *
     * @param type 被检查的类型，必须是已经确定泛型类型的类型
     *
     * @return {@link Type}，可能为 {@code null}
     */
    public static Type getTypeArgument(Type type) {
        return getTypeArgument(type, 0);
    }

    /**
     * 获得给定类的泛型参数
     *
     * @param type 被检查的类型，必须是已经确定泛型类型的类
     * @param index 泛型类型的索引号，即第几个泛型类型
     *
     * @return {@link Type}
     */
    public static Type getTypeArgument(Type type, int index) {
        final Type[] typeArguments = getTypeArguments(type);
        if (null != typeArguments && typeArguments.length > index) {
            return typeArguments[index];
        }
        return null;
    }

    /**
     * 获得指定类型中所有泛型参数类型，例如：
     *
     * <pre>
     * class A&lt;T&gt;
     * class B extends A&lt;String&gt;
     * </pre>
     * <p>
     * 通过此方法，传入 B.class 即可得到 String
     *
     * @param type 指定类型
     *
     * @return 所有泛型参数类型
     */
    public static Type[] getTypeArguments(Type type) {
        if (null == type) {
            return null;
        }

        final ParameterizedType parameterizedType = toParameterizedType(type);
        return (null == parameterizedType) ? null : parameterizedType.getActualTypeArguments();
    }

    /**
     * 将 {@link Type} 转换为 {@link ParameterizedType}<br>
     * {@link ParameterizedType} 用于获取当前类或父类中泛型参数化后的类型<br>
     * 一般用于获取泛型参数具体的参数类型，例如：
     *
     * <pre>
     * class A&lt;T&gt;
     * class B extends A&lt;String&gt;
     * </pre>
     * <p>
     * 通过此方法，传入 B.class 即可得到 B{@link ParameterizedType}，从而获取到 String
     *
     * @param type {@link Type}
     *
     * @return {@link ParameterizedType}
     */
    public static ParameterizedType toParameterizedType(Type type) {
        if (type instanceof ParameterizedType) {
            return (ParameterizedType) type;
        } else if (type instanceof Class) {
            return toParameterizedType(((Class<?>) type).getGenericSuperclass());
        }
        return null;
    }

    /**
     * 获取指定泛型变量对应的真实类型<br>
     * 由于子类中泛型参数实现和父类（接口）中泛型定义位置是一一对应的，因此可以通过对应关系找到泛型实现类型<br>
     * 使用此方法注意：
     *
     * <pre>
     * 1. superClass 必须是 clazz 的父类或者 clazz 实现的接口
     * 2. typeVariable 必须在 superClass 中声明
     * </pre>
     *
     * @param actualType 真实类型所在类，此类中记录了泛型参数对应的实际类型
     * @param typeDefineClass 泛型变量声明所在类或接口，此类中定义了泛型类型
     * @param typeVariables 泛型变量，需要的实际类型对应的泛型参数
     *
     * @return 给定泛型参数对应的实际类型，如果无对应类型，返回 null
     */
    public static Type[] getActualTypes(Type actualType, Class<?> typeDefineClass, Type... typeVariables) {
        if (!typeDefineClass.isAssignableFrom(getClass(actualType))) {
            throw new IllegalArgumentException("Parameter [superClass] must be assignable from [clazz]");
        }

        // 泛型参数标识符列表
        final TypeVariable<?>[] typeVars = typeDefineClass.getTypeParameters();
        if (ArrayUtils.isEmpty(typeVars)) {
            return null;
        }
        // 实际类型列表
        final Type[] actualTypeArguments = TypeUtils.getTypeArguments(actualType);
        if (ArrayUtils.isEmpty(actualTypeArguments)) {
            return null;
        }

        int size = Math.min(actualTypeArguments.length, typeVars.length);
        final Map<TypeVariable<?>, Type> tableMap = new TableMap<>(typeVars, actualTypeArguments);

        // 查找方法定义所在类或接口中此泛型参数的位置
        final Type[] result = new Type[size];
        for (int i = 0; i < typeVariables.length; i++) {
            //noinspection SuspiciousMethodCalls
            result[i] = (typeVariables[i] instanceof TypeVariable) ? tableMap.get(typeVariables[i]) : typeVariables[i];
        }
        return result;
    }

    /**
     * 获取指定泛型变量对应的真实类型<br>
     * 由于子类中泛型参数实现和父类（接口）中泛型定义位置是一一对应的，因此可以通过对应关系找到泛型实现类型<br>
     * 使用此方法注意：
     *
     * <pre>
     * 1. superClass 必须是 clazz 的父类或者 clazz 实现的接口
     * 2. typeVariable 必须在 superClass 中声明
     * </pre>
     *
     * @param actualType 真实类型所在类，此类中记录了泛型参数对应的实际类型
     * @param typeDefineClass 泛型变量声明所在类或接口，此类中定义了泛型类型
     * @param typeVariable 泛型变量，需要的实际类型对应的泛型参数
     *
     * @return 给定泛型参数对应的实际类型
     */
    public static Type getActualType(Type actualType, Class<?> typeDefineClass, Type typeVariable) {
        Type[] types = getActualTypes(actualType, typeDefineClass, typeVariable);
        if (ArrayUtils.isNotEmpty(types)) {
            return types[0];
        }
        return null;
    }

    /**
     * 是否未知类型<br>
     * type 为 null 或者 {@link TypeVariable} 都视为未知类型
     *
     * @param type Type 类型
     *
     * @return 是否未知类型
     */
    public static boolean isUnknown(Type type) {
        return null == type || type instanceof TypeVariable;
    }

    /**
     * 指定泛型数组中是否含有泛型变量
     *
     * @param types 泛型数组
     *
     * @return 是否含有泛型变量
     */
    public static boolean hasTypeVariable(Type... types) {
        for (Type type : types) {
            if (type instanceof TypeVariable) {
                return true;
            }
        }
        return false;
    }

}
